package com.chy.blog_web.modular.sharCore;

import cn.hutool.core.date.ChineseDate;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chy.blog_web.core.el.ELSession;
import com.chy.blog_web.core.util.DateUtil;
import com.chy.blog_web.core.util.SendSmsUtil;
import com.chy.blog_web.core.util.StringUtil;
import com.chy.blog_web.modular.web.entity.BoInfoSendMrg;
import com.chy.blog_web.modular.web.entity.BoMarkDate;
import com.chy.blog_web.modular.web.entity.BoMsgSign;
import com.chy.blog_web.modular.web.entity.BoMsgTemplate;
import com.chy.blog_web.modular.web.service.IBoInfoSendMrgService;
import com.chy.blog_web.modular.web.service.IBoMarkDateService;
import com.chy.blog_web.modular.web.service.IBoMsgSignService;
import com.chy.blog_web.modular.web.service.IBoMsgTemplateService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 定时器
 * @Component注解用于对那些比较中立的类进行注释；
 * 相对与在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释
 */
@Component
@EnableScheduling   // 1.开启定时任务
@EnableAsync        // 2.开启多线程
@ConditionalOnExpression("#{'pro'.equals(environment['spring.profiles.active'])}") //正式环境才开启
public class ScheduleTask {

    Logger logger = LoggerFactory.getLogger(ScheduleTask.class);

    @Autowired
    private IBoMarkDateService markDateService;

    @Autowired
    private IBoMsgTemplateService msgTemplateService;

    @Autowired
    private IBoMsgSignService msgSignService;

    @Autowired
    private IBoInfoSendMrgService infoSendMrgService;

    /**
     * 节日提醒定时器-早上八点执行
     */
    @Async
//    @Scheduled(cron = "0 23 15 * * ?") //几点执行
    @Scheduled(cron = "0 0 8 * * ?")
    public void markDateTaskAmEight(){
        try{
            logger.info(">>>>>>>>>节日提醒定时器-早上八点执行-开始");

            //获取签名
            BoMsgSign msgSign = msgSignService.getOne(new QueryWrapper<BoMsgSign>().eq("use_status", 0));
            String singName = SendSmsUtil.SIGNNAME_CODE;
            if(ObjectUtil.isNotEmpty(msgSign)){
                singName = msgSign.getSignName();
            }

            //获取提醒日期---早上八点
            List<BoMarkDate> markDateList = markDateService.getBaseMapper()
                    .selectList(new QueryWrapper<BoMarkDate>()
                            .eq("is_open", 1)
                            .eq("run_time_type", 0)
                            .eq("del_flag", 0).orderByAsc("mark_date"));
            logger.info(">>>>>>>>>节日提醒定时器-早上八点执行-记录数：{}", markDateList.size());

            String currentDate = DateUtil.getSystemDate(); //当前日期
            for(BoMarkDate markDate: markDateList){
                //发送短信
                sendMsg(markDate, currentDate, singName, "");
            }
            logger.info(">>>>>>>>>节日提醒定时器-早上八点执行-结束");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 节日提醒定时器-早上九点执行
     */
    @Async
//    @Scheduled(cron = "0 26 15 * * ?") //几点执行
    @Scheduled(cron = "0 0 9 * * ?")
    public void markDateTaskAmNine(){
        try{
            logger.info(">>>>>>>>>节日提醒定时器-早上九点执行-开始");

            //获取签名
            BoMsgSign msgSign = msgSignService.getOne(new QueryWrapper<BoMsgSign>().eq("use_status", 0));
            String singName = SendSmsUtil.SIGNNAME_CODE;
            if(ObjectUtil.isNotEmpty(msgSign)){
                singName = msgSign.getSignName();
            }

            //获取提醒日期---早上九点
            List<BoMarkDate> markDateList = markDateService.getBaseMapper()
                    .selectList(new QueryWrapper<BoMarkDate>()
                            .eq("is_open", 1)
                            .eq("run_time_type", 1)
                            .eq("del_flag", 0).orderByAsc("mark_date"));
            logger.info(">>>>>>>>>节日提醒定时器-早上九点执行-记录数：{}", markDateList.size());

            String currentDate = DateUtil.getSystemDate(); //当前日期
            for(BoMarkDate markDate: markDateList){
                //发送短信
                sendMsg(markDate, currentDate, singName, "");
            }
            logger.info(">>>>>>>>>节日提醒定时器-早上九点执行-结束");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 节日提醒定时器-下午六点执行
     */
    @Async
//    @Scheduled(cron = "0 28 15 * * ?") //几点执行
    @Scheduled(cron = "0 0 18 * * ?")
    public void markDateTaskPmSix(){
        try{
            logger.info(">>>>>>>>>节日提醒定时器-下午六点执行-开始");

            //获取签名
            BoMsgSign msgSign = msgSignService.getOne(new QueryWrapper<BoMsgSign>().eq("use_status", 0));
            String singName = SendSmsUtil.SIGNNAME_CODE;
            if(ObjectUtil.isNotEmpty(msgSign)){
                singName = msgSign.getSignName();
            }

            //获取提醒日期---下午六点
            List<BoMarkDate> markDateList = markDateService.getBaseMapper()
                    .selectList(new QueryWrapper<BoMarkDate>()
                            .eq("is_open", 1)
                            .eq("run_time_type", 2)
                            .eq("del_flag", 0).orderByAsc("mark_date"));
            logger.info(">>>>>>>>>节日提醒定时器-下午六点执行-记录数：{}", markDateList.size());

            String currentDate = DateUtil.getSystemDate(); //当前日期
            for(BoMarkDate markDate: markDateList){
                //发送短信
                sendMsg(markDate, currentDate, singName, "");
            }
            logger.info(">>>>>>>>>节日提醒定时器-下午六点执行-结束");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 节日提醒定时器-晚上八点执行
     */
    @Async
//    @Scheduled(cron = "0 29 15 * * ?") //几点执行
    @Scheduled(cron = "0 0 20 * * ?")
    public void markDateTaskPmEight(){
        try{
            logger.info(">>>>>>>>>节日提醒定时器-晚上八点执行-开始");

            //获取签名
            BoMsgSign msgSign = msgSignService.getOne(new QueryWrapper<BoMsgSign>().eq("use_status", 0));
            String singName = SendSmsUtil.SIGNNAME_CODE;
            if(ObjectUtil.isNotEmpty(msgSign)){
                singName = msgSign.getSignName();
            }

            //获取提醒日期---晚上八点
            List<BoMarkDate> markDateList = markDateService.getBaseMapper()
                    .selectList(new QueryWrapper<BoMarkDate>()
                            .eq("is_open", 1)
                            .eq("run_time_type", 3)
                            .eq("del_flag", 0).orderByAsc("mark_date"));
            logger.info(">>>>>>>>>节日提醒定时器-晚上八点执行-记录数：{}", markDateList.size());

            String currentDate = DateUtil.getSystemDate(); //当前日期
            for(BoMarkDate markDate: markDateList){
                //发送短信
                sendMsg(markDate, currentDate, singName, "");
            }
            logger.info(">>>>>>>>>节日提醒定时器-晚上八点执行-结束");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 查询运营商发送短信状态
     */
    @Async
//    @Scheduled(cron = "0 50 16 * * ?") //几点执行
//    @Scheduled(cron = "0 0 0/3 * * ? ") //每3个小时执行一次
    @Scheduled(cron = "0 0 10,14,19,21,22 * * ? ")  //每天10,14,19,21,22点执行
    public void getMsgState(){
        try{
            logger.info(">>>>>>>>>查询运营商发送短信状态-开始");
            //获取签名
            BoMsgSign msgSign = msgSignService.getOne(new QueryWrapper<BoMsgSign>().eq("use_status", 0));
            String singName = SendSmsUtil.SIGNNAME_CODE;
            if(ObjectUtil.isNotEmpty(msgSign)){
                singName = msgSign.getSignName();
            }

            String threeDayTime = DateUtil.getLastDay(DateUtil.getSystemDate(), 0, 0, -5) + " 00:00:00"; //5天前时间
            String currentDate = DateUtil.getSystemDate(); //当前日期
            String currentDateStr = DateUtil.getSystemDate().replaceAll("-", ""); //当前日期，格式：yyyyMMdd

            //获取信息发送记录(5天内发送状态：等待回执)
            List<BoInfoSendMrg> infoSendMrgList = infoSendMrgService.getBaseMapper().selectList(new QueryWrapper<BoInfoSendMrg>()
                    .eq("send_result", 1)
                    .ge("send_date", threeDayTime)
                    .orderByAsc("send_date"));
            logger.info(">>>>>>>>>查询运营商发送短信状态-发送记录[等待回执]记录数：{}", infoSendMrgList.size());
            for(BoInfoSendMrg infoSendMrg : infoSendMrgList){
                if(StringUtil.notNull(infoSendMrg.getBizId()) && infoSendMrg.getSendResult() == 1 && StringUtil.notNull(infoSendMrg.getReceivePhone())){
                    String[] phoneArr = infoSendMrg.getReceivePhone().split(",");
                    Date sendTime = infoSendMrg.getSendDate();
                    String sendDate = DateUtil.getStringDate(sendTime).replaceAll("-", ""); //日期格式：yyyyMMdd

                    String resultJson = SendSmsUtil.getMsgState(phoneArr[0], infoSendMrg.getBizId(), sendDate);
                    Map<String, Object> resultMap = JSONUtil.parseObj(resultJson);
                    JSONObject bodyObject = JSONUtil.parseObj(resultMap.get("body")); //获取返回值
                    logger.info(">>>>>>>>>查询运营商发送短信状态-返回值body：{}", bodyObject);
                    JSONObject smsSendDetailDTOs = JSONUtil.parseObj(bodyObject.get("smsSendDetailDTOs")); //运营商发送明细
                    logger.info(">>>>>>>>>查询运营商发送短信状态-返回值smsSendDetailDTOs：{}", smsSendDetailDTOs);
                    if(ObjectUtil.isNotEmpty(smsSendDetailDTOs)){
                        JSONArray smsSendDetailDTOArray = JSONUtil.parseArray(smsSendDetailDTOs.get("smsSendDetailDTO"));
                        if(!smsSendDetailDTOArray.isEmpty()){
                            JSONObject smsSendDetailDTO = smsSendDetailDTOArray.getJSONObject(0);
                            int sendStatus = smsSendDetailDTO.getInt("sendStatus"); //发送状态
                            String errCode = smsSendDetailDTO.getStr("errCode"); //错误代码
                            logger.info(">>>>>>>>>查询运营商发送短信状态-返回值[错误代码]：{}", errCode);
                            logger.info(">>>>>>>>>查询运营商发送短信状态-返回值[发送状态]：{}", sendStatus);
                            if(sendStatus == 1){ //等待回执
                                logger.info(">>>>>>>>>查询运营商发送短信状态-返回值[发送状态]名称：等待回执");
                                logger.info(">>>>>>>>>查询运营商发送短信状态-不做任何操作");
                                //发送成功,更新状态
                            }else if(sendStatus == 3){
                                logger.info(">>>>>>>>>查询运营商发送短信状态-返回值[发送状态]名称：发送成功");
                                logger.info(">>>>>>>>>查询运营商发送短信状态-更新状态");
                                infoSendMrg.setSendResult(sendStatus); //发送状态(1等待回执,2发送失败,3发送成功)
                                infoSendMrgService.updateById(infoSendMrg);

                                //发送失败,如果是当天失败的，则重新发送并且更新状态
                            } else if(sendStatus == 2){
                                logger.info(">>>>>>>>>查询运营商发送短信状态-返回值[发送状态]名称：发送失败");
                                logger.info(">>>>>>>>>查询运营商发送短信状态-如果是当天失败的，则重新发送并且更新状态");
                                infoSendMrg.setSendResult(sendStatus); //发送状态(1等待回执,2发送失败,3发送成功)
                                infoSendMrg.setErrorContent(errCode + "(" + SendSmsUtil.errorMap.get(errCode) + ")");
                                if(sendDate.equals(currentDateStr) && StringUtil.notNull(infoSendMrg.getMarkId())){
                                    logger.info(">>>>>>>>>查询运营商发送短信状态-重新发送，指定发送手机号码：{}", infoSendMrg.getReceivePhone());
                                    //获取提醒日期
                                    BoMarkDate markDate = markDateService.getById(infoSendMrg.getMarkId());
                                    //发送短信
                                    sendMsg(markDate, currentDate, singName, infoSendMrg.getReceivePhone());
                                    infoSendMrg.setAgainSend(1); //已再次发送(0否,1是)
                                }
                                infoSendMrgService.updateById(infoSendMrg);
                            }
                        }
                    }
                }
            }
            logger.info(">>>>>>>>>查询运营商发送短信状态-结束");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 发送短信
     * @param markDate 节日
     * @param currentDate 当前日期
     * @param singName 签名
     * @param toPhone 指定发送手机号
     */
    public void sendMsg(BoMarkDate markDate, String currentDate, String singName, String toPhone){
        try{
            Date md = markDate.getMarkDate();
            logger.info(">>>>>>>>>节日提醒定时器-节日名称：{},日期：{}", markDate.getTitle(), DateUtil.getStringDate(md));
            //使用hutool工具类进行农历转成公历，原本为公历则不转
            if(markDate.getDateType() == 1){
                Calendar cal = Calendar.getInstance();
                cal.setTime(markDate.getMarkDate());
                ChineseDate chineseDate = new ChineseDate(cal.get(Calendar.YEAR),cal.get(Calendar.MONTH) + 1,cal.get(Calendar.DAY_OF_MONTH));
                //通过反射获取private属性,拿到转成公历后的日期
                Field gyear = chineseDate.getClass().getDeclaredField("gyear");
                Field gmonth = chineseDate.getClass().getDeclaredField("gmonth");
                Field gday = chineseDate.getClass().getDeclaredField("gday");
                gyear.setAccessible(true);
                gmonth.setAccessible(true);
                gday.setAccessible(true);

                cal.set(Calendar.YEAR, (int) gyear.get(chineseDate));
                cal.set(Calendar.MONTH, (int) gmonth.get(chineseDate) - 1);
                cal.set(Calendar.DAY_OF_MONTH, (int) gday.get(chineseDate));

                md = cal.getTime();
            }

            //获取转换后的节日提醒日期
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(md);
            String year = calendar.get(Calendar.YEAR) + "";
            String month = (calendar.get(Calendar.MONTH) + 1) < 10? "0" + (calendar.get(Calendar.MONTH) + 1) : "" + (calendar.get(Calendar.MONTH) + 1);
            String day = calendar.get(Calendar.DAY_OF_MONTH) + "";
            String mDate = year + "-" + month + "-" + day;
            logger.info(">>>>>>>>>节日提醒定时器-节日名称：{},处理后日期：{}", markDate.getTitle(), mDate);

            //节日日期未过
            if(!DateUtil.isBefore(mDate)){
                //获当前日期和提醒日期天数差
                int dayNum = DateUtil.getDays(mDate, currentDate);
                logger.info(">>>>>>>>>节日提醒定时器-距离天数：{}", dayNum);

                //发送次数(0五天三次,1五天两次,2当天发送)：五天三次-第5,3,0天发送, 五天两次-第5,0天发送, 当天发送-日期当天发送
                boolean sendFlag = false; //是否发送标记
                if(markDate.getSendTime() == 2){ //当天发送
                    if(dayNum == 0){
                        sendFlag = true;
                    }
                }else if(markDate.getSendTime() == 1){ //第5,0天发送
                    if(dayNum == 0 || dayNum == 5){
                        sendFlag = true;
                    }
                }else if(dayNum == 5 || dayNum == 3 || dayNum == 0){ //第5,3,0天发送
                    sendFlag = true;
                }

                if(sendFlag){
                    //------根据节日提醒类型、是否当天获取对应的短信模板：提醒类型(0生日提醒，1纪念节日，2普通节日，3备忘事项)
                    String temCode = SendSmsUtil.TEMPLATE_CODE_GENERAL; //节日通用模板-新 模板编号

                    //本人接收-短信模板变量
                    JSONObject personJson = JSONUtil.createObj()
                            .putOpt("time", mDate)
                            .putOpt("holidays", markDate.getTopic())
                            .putOpt("dayNum", dayNum);

                    //额外提醒-短信模板变量
                    JSONObject remindJson = JSONUtil.createObj()
                            .putOpt("time", mDate)
                            .putOpt("holidays", markDate.getOtherTopic())
                            .putOpt("dayNum", dayNum);

                    if(dayNum == 0){
                        if(markDate.getRemarkType() == 0 || markDate.getRemarkType() == 1){ //生日提醒、纪念节日
                            //生日/纪念日-当天 模板编号
                            temCode = SendSmsUtil.TEMPLATE_CODE_MARK_DATE_CURRENT;

                            //本人接收-短信模板变量
                            personJson = JSONUtil.createObj().putOpt("dayName", markDate.getTopic());

                            //额外提醒-短信模板变量
                            remindJson = JSONUtil.createObj().putOpt("dayName", markDate.getOtherTopic());
                        }else if(markDate.getRemarkType() == 2){ //普通节日
                            //普通节日-当天 模板编号
                            temCode = SendSmsUtil.TEMPLATE_CODE_COMMON_DATE_CURRENT;

                            //本人接收-短信模板变量
                            personJson = JSONUtil.createObj().putOpt("dayName", markDate.getTopic());

                            //额外提醒-短信模板变量
                            remindJson = JSONUtil.createObj().putOpt("dayName", markDate.getOtherTopic());
                        }else if(markDate.getRemarkType() == 3){ //备忘事项
                            //备忘事项-当天 模板编号
                            temCode = SendSmsUtil.TEMPLATE_CODE_BACKLOG_DATE_CURRENT;

                            //本人接收-短信模板变量
                            personJson = JSONUtil.createObj().putOpt("title", markDate.getTopic());

                            //额外提醒-短信模板变量
                            remindJson = JSONUtil.createObj().putOpt("title", markDate.getOtherTopic());
                        }
                    }
                    //获取模板信息
                    BoMsgTemplate msgTemplate = msgTemplateService.getOne(new QueryWrapper<BoMsgTemplate>().eq("code", temCode));

                    if(ObjectUtil.isNotEmpty(msgTemplate)){
                        logger.info(">>>>>>>>>节日提醒定时器-开始发送短信");
                        //**************************发送短信********************

                        String personPhone = markDate.getPersonPhone(); //本人提醒手机号
                        String remindPhone = markDate.getRemindPhone(); //额外提醒手机号

                        //给本人发送短信提醒,如果有指定手机号则判断是否一样的才发送
                        if(StringUtil.notNull(personPhone) && StringUtil.notNull(markDate.getTopic())){
                            if(StringUtil.notNull(toPhone) && personPhone.equals(toPhone) || StringUtil.isNull(toPhone)){
                                String templateParam = JSONUtil.toJsonStr(personJson);
                                logger.info(">>>>>>>>>节日提醒定时器-开始给本人发送短信-短信模板变量templateParam：{}", templateParam);
                                logger.info(">>>>>>>>>节日提醒定时器-开始给本人发送短信-手机号码personPhone：{}", personPhone);
                                logger.info(">>>>>>>>>节日提醒定时器-开始给本人发送短信-内容：{}", msgTemplate.getContent());

                                String resultJson = SendSmsUtil.SendSms(personPhone, singName, temCode, templateParam);

                                logger.info(">>>>>>>>>节日提醒定时器-开始给本人发送短信-发送结果：{}", resultJson);

                                //生成信息发送记录
                                infoSendMrgService.saveMsgLog(resultJson, msgTemplate, personJson, markDate.getPersonName(), personPhone, markDate.getRemarkType(), markDate.getId());
                            }

                        }

                        //额外发送短信提醒,如果有指定手机号则判断是否一样的才发送
                        if(StringUtil.notNull(remindPhone) && StringUtil.notNull(markDate.getOtherTopic())){
                            //字段格式：陈陈陈,苗;18312161134,16212011712
                            String[] peAndPhoneArr = remindPhone.split(";");
                            String[] sendAndPhoneNameArr = peAndPhoneArr[0].split(",");
                            String[] sendAndPhoneArr = peAndPhoneArr[1].split(",");
                            for(int i = 0; i < sendAndPhoneArr.length; i++){
                                if(StringUtil.notNull(toPhone) && sendAndPhoneArr[i].equals(toPhone) || StringUtil.isNull(toPhone)){
                                    String templateParam = JSONUtil.toJsonStr(remindJson);
                                    logger.info(">>>>>>>>>节日提醒定时器-开始给额外接收人发送短信-短信模板变量templateParam：{}", templateParam);
                                    logger.info(">>>>>>>>>节日提醒定时器-开始给额外接收人发送短信-手机号码personPhone：{}", sendAndPhoneArr[i]);
                                    logger.info(">>>>>>>>>节日提醒定时器-开始给额外接收人发送短信-内容：{}", msgTemplate.getContent());

                                    String resultJson = SendSmsUtil.SendSms(sendAndPhoneArr[i], singName, temCode, templateParam);

                                    logger.info(">>>>>>>>>节日提醒定时器-开始给额外接收人发送短信-发送结果：{}", resultJson);

                                    //生成信息发送记录
                                    infoSendMrgService.saveMsgLog(resultJson, msgTemplate, remindJson, sendAndPhoneNameArr[i], sendAndPhoneArr[i], markDate.getRemarkType(), markDate.getId());
                                }
                            }
                        }
                    }else{
                        logger.info(">>>>>>>>>节日提醒定时器-短信模板不存在，无法发送短信：{}", temCode);
                    }
                }
            }else{
                logger.info(">>>>>>>>>节日提醒定时器-{} 节日已过", mDate);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * JSONObject转map
     * @param jsonObject
     * @return
     */
    public static Map<String, Object> convertToMap(JSONObject jsonObject) {
        Map<String, Object> map = new HashMap<>();
        for(String key: jsonObject.keySet()){
            Object value = jsonObject.get(key);
            map.put(key, value);
        }
        return map;
    }
}
